#ifndef MOCHA_DETAIL_COLOR_HPP_INCLUDED
#define MOCHA_DETAIL_COLOR_HPP_INCLUDED

/**
 * @file 
 */

#include <cstddef>
#include <cstdint>
#include <boost/static_assert.hpp>
#include "mocha/enum_string.hpp"
#include "mocha/memory.hpp"

namespace mocha { namespace graphics {
/// Color channel type.
typedef std::uint8_t rgba_chan_t;
/// Color value type.
typedef std::uint32_t rgba_t;
BOOST_STATIC_ASSERT(((4 * sizeof(rgba_chan_t)) == sizeof(rgba_t))); // Ensure the channel size is correct.
/// White color value.
const rgba_t color_white = 0xFFFFFFFF;
/// White (transparent) color value.
const rgba_t color_white_alpha = 0xFFFFFF00;
/// Black color value.
const rgba_t color_black = 0x000000FF;
/// Black (transparent) color value.
const rgba_t color_black_alpha = 0x00000000;
/// Red color value.
const rgba_t color_red = 0xFF0000FF;
/// Green color value.
const rgba_t color_green = 0x00FF00FF;
/// Blue color value.
const rgba_t color_blue = 0x0000FFFF;
/**
 * 32-bit RGBA color.
 * Provides access to individual channels via multiple views (members).
 * Implicitly assigns/converts to an RGBA 32-bit integer type.
 * @see rgba_t
 * @see rgba_chan_t
 */
struct color {
	color(): value(color_black) {}
	/**
	 * @param red 
	 * @param green 
	 * @param blue 
	 * @param alpha 
	 */
	color(rgba_chan_t red, rgba_chan_t green, rgba_chan_t blue, rgba_chan_t alpha = 0xFF): red(red), green(green), blue(blue), alpha(alpha) {}
	/**
	 * @param value 
	 */
	color(rgba_t value): value(value) {}
	rgba_t& operator=(rgba_t& rhs) {
		return value = rhs;
	}
	operator rgba_t() const {
		return value;
	}
	union {
		struct {
			/// Red channel.
			rgba_chan_t red;
			/// Green channel.
			rgba_chan_t green;
			/// Blue channel.
			rgba_chan_t blue;
			/// Alpha channel.
			rgba_chan_t alpha;
		};
		/// Color channels.
		rgba_chan_t channels[4];
		/// 32-bit color value.
		rgba_t value;
	};
};
BOOST_STATIC_ASSERT((sizeof(color) == sizeof(rgba_t))); // Ensure that color is the same size as an RGBA color value.
/**
 * Pixel data types.
 */
enum pixel_type {
	/// Byte pixel data type.
	pixel_type_byte = 1 << 0,
	/// Unsigned byte pixel data type.
	pixel_type_ubyte = 1 << 1,
	/// Integer (32-bit) pixel data type.
	pixel_type_int = 1 << 2,
	/// Unsigned integer (32-bit) pixel data type.
	pixel_type_uint = 1 << 3,
	/// Single-precision floating point pixel data type.
	pixel_type_float = 1 << 4,
	/// Double-precision floating point pixel data type.
	pixel_type_double = 1 << 5
};
/**
 * Generalized pixel data type masks.
 */
enum {
	/// 
	pixel_type_any_int = pixel_type_byte | pixel_type_ubyte | pixel_type_int | pixel_type_uint,
	/// 
	pixel_type_any_float = pixel_type_float | pixel_type_double
};
/**
 * Pixel format.
 */
struct pixel_format {
	/// Data type used to store each channel value.
	pixel_type type;
	union {
		struct {
			/**
			 * Channel mask.
			 * Only used with integer pixel data types.
			 * Indicates the size and position of each channel within each pixel value.
			 * A value of zero means the channel is absent.
			 * Channels should always be contiguous. Non-contiguous channels are considered an error.
			 * @see type 
			 */
			struct mask {
				/// Red integer channel mask.
				std::uint32_t red;
				/// Green integer channel mask.
				std::uint32_t green;
				/// Blue integer channel mask.
				std::uint32_t blue;
				/// Alpha integer channel mask.
				std::uint32_t alpha;
			};
			/**
			 * Channel precision loss.
			 * Only used with integer pixel data types.
			 * @see type
			 */
			struct loss {
				/// 
				std::uint8_t red;
				/// 
				std::uint8_t green;
				/// 
				std::uint8_t blue;
				/// 
				std::uint8_t alpha;
			};
		};
		/**
		 * Channel indecies.
		 * Only used with floating point pixel data types.
		 * Indicates the order/position of each channel within each pixel value.
		 * A negative value means the channel is absent.
		 * @see type
		 */
		struct index {
			/// Red floating point channel index.
			std::int8_t red;
			/// Green floating point channel index.
			std::int8_t green;
			/// Blue floating point channel index.
			std::int8_t blue;
			/// Alpha floating point channel index.
			std::int8_t alpha;
		};
	};
};
/**
 * @param fmt Pixel format.
 * @return Bytes per pixel.
 */
inline size_t bytes_per_pixel(const pixel_format& fmt) {
	return 4u; // FIXME: Base the size on the number and size of channels in the pixel format.
}
/**
 * @param format Pixel format.
 * @return Bits per pixel.
 */
inline size_t bits_per_pixel(const pixel_format& fmt) {
	return 8 * bytes_per_pixel(fmt);
}
}}
MOCHA_ENUM_STRING_BEGIN(mocha::graphics::pixel_type) {
	MOCHA_ENUM_STRING(pixel_type_byte);
	MOCHA_ENUM_STRING(pixel_type_ubyte);
	MOCHA_ENUM_STRING(pixel_type_int);
	MOCHA_ENUM_STRING(pixel_type_uint);
	MOCHA_ENUM_STRING(pixel_type_float);
	MOCHA_ENUM_STRING(pixel_type_double);
	MOCHA_ENUM_ALIAS(pixel_type_byte, "byte");
	MOCHA_ENUM_ALIAS(pixel_type_ubyte, "ubyte");
	MOCHA_ENUM_ALIAS(pixel_type_int, "int");
	MOCHA_ENUM_ALIAS(pixel_type_uint, "uint");
	MOCHA_ENUM_ALIAS(pixel_type_float, "float");
	MOCHA_ENUM_ALIAS(pixel_type_double, "double");
} MOCHA_ENUM_STRING_END;

#endif

